#include <alloc.h>
#include <stdio.h>
#include <conio.h>
#include <assert.h>
#include "InstInfo.h"
#include "Variable.h"
char VariableTypes[VAR_UNKNOWN+1][10] = {"char","int","long","void","proc",\
			"proc","struct","union","typedef","int","unknown"};
int Variable::SameKind(Variable &v)
{
  switch(Operand)
  {
    case OPER_UNKNOWN : return 0;
    case REG_DIRECT :
    case IMMEDIATE  :
    case MEMORY     : return Operand==v.Operand;
    // for other cases,...
    default	    : return Operand==v.Operand && Data1==v.Data1;
  }
}
int Variable::IsCVariable()
{
  switch(Operand)
  {
    case REG_DIRECT : return 1; // for register variables.
    case INDEXED_BYTE : case INDEXED_WORD : if (Data1!=INDX_BP) return 0;
    case MEMORY : case IMMEDIATE : return 1;
    default : return 0;
  }
}
void Variable::operator = (Variable &v)
{
  Operand=v.Operand;
  Data1=v.Data1;
  Data2=v.Data2;
}
Variable &Variable::operator - (int offset)
{
  static Variable v;
  v=*this;
  switch(Operand)
  {
    case REG_DIRECT : break;
    case MEMORY : v.Data1-=offset; break;
    case INDEXED_BYTE :
    case INDEXED_WORD :
	  if (v.Data1==INDX_BP) v.Data2-=offset;
	  break;
    default : assert("Address of variable"!="Supported mode");
  }
  return v;
}
int Variable::operator == (Variable &v)
{
  return (Operand==v.Operand && Data1==v.Data1 && Data2==v.Data2);
}
Word Variable::GetAddress()
{
  switch(Operand)
  {
    case MEMORY : return Data1;
    case INDEXED_BYTE :
    case INDEXED_WORD :
	  if (Data1==INDX_BP) return Data2;
	  return 0;
    case REG_DIRECT : return 0;
    default : assert("Address of variable"!="Supported mode");
  }
  return 0;
}
Variable::operator char *()
{
  InstInfo inst;
  inst.SetVariable(*this,2);
  String tt;
  static char tname[10];
  tname[0]=0;
  switch(Operand)
  {
    case MEMORY :
    case INDEXED_BYTE :
    case INDEXED_WORD :
	strcpy(tname,(char *)inst.GetOperand2(NULL,NULL)); break;
    case IMMEDIATE    : // variable is containing address of a function.
	tt=(Word)Data2;
	strcpy(tname,(char *)tt);
	strcat(tname,":"); tt=(Word)Data1;
	strcat(tname,(char *)tt); break;
  }
  return tname;
}
VariableList::VariableList()
{
  Data.Operand=OPER_UNKNOWN;
  Next=NULL;
}
VariableList::~VariableList()
{
  Data.Operand=OPER_UNKNOWN;
  if (Next) delete Next;
  Next=NULL;
}
void VariableList::Display()
{
  VariableList *ptr=this;
  while(ptr && ptr->Data.Operand!=OPER_UNKNOWN)
  {
    printf("%s is %s of type %s\n",(char *)ptr->Data,ptr->Name,VariableTypes[ptr->Type]);
    ptr=ptr->Next;
  }
}
void VariableList::Add(Variable &v,int _type,int _size,char *_name,int _readonly)
{
  if (!v.IsCVariable()) return;
  VariableList *ptr=this;
  while(ptr->Data.Operand!=OPER_UNKNOWN)
  {
    // If the same variable is present, just replace the type, name, etc.
    if (ptr->Data==v)
    {
      if (ptr->readOnly) return;
      break;
    }
    if (ptr->Next==NULL)
    {
      ptr->Next = new VariableList;
      assert(ptr);
      ptr = ptr->Next;
      break;
    }
    ptr=ptr->Next;
  }
  ptr->Data=v;
  ptr->Type=_type;
  ptr->Size=_size;
  if (_name) strcpy(ptr->Name,_name);
  else strcpy(ptr->Name,"");
  readOnly=_readonly;
}
int VariableList::GetType(Variable &v)
{
  VariableList *ptr=this;
  while(ptr!=NULL && ptr->Data.Operand!=OPER_UNKNOWN)
  {
    if (ptr->Data==v) return ptr->Type;
    ptr=ptr->Next;
  }
  return VAR_UNKNOWN;
}
void VariableList::Del(Variable &v)
{
  VariableList *ptr=this;
  VariableList *parent=this;
  if (Data==v)
  {
    if (readOnly) return;
    if (Next)
    {
      memcpy(this,Next,sizeof(VariableList));
      ptr=ptr->Next;
    }
    else
    {
      Data.Operand=OPER_UNKNOWN; return;
    }
  } else ptr=ptr->Next;
  do
  {
    while(ptr!=NULL && ptr->Data.Operand!=OPER_UNKNOWN)
    {
      if (ptr->Data==v)
      {
	if (ptr->readOnly) return;
	break;
      }
      parent=ptr;
      ptr=ptr->Next;
    }
    if (ptr!=NULL && ptr->Data.Operand!=OPER_UNKNOWN)
    {
      parent->Next=ptr->Next;
      ptr->Next=NULL;
      delete ptr;
      ptr=parent->Next;
    }
  }while(ptr!=NULL && ptr->Data.Operand!=OPER_UNKNOWN);
}
char *VariableList::GetName(Variable &v,int _type)
{
  VariableList *ptr=this;
  while(ptr!=NULL && ptr->Data.Operand!=OPER_UNKNOWN)
  {
    if (ptr->Data==v)
    {
      // check Type only if requested to.
      if (_type>=0 && _type!=Type) goto checknext;
      if (strcmp(ptr->Name,"")==0) return NULL;
      return ptr->Name;
    }
  checknext:
    ptr=ptr->Next;
  }
  return NULL;
}
int VariableList::IsPresent(Variable &v,int _type)
{
  VariableList *ptr=this;
  while(ptr!=NULL && ptr->Data.Operand!=OPER_UNKNOWN)
  {
    if (ptr->Data==v)
    {
      // check Type only if requested to.
      if (_type>=0 && _type!=Type) continue;
      return 1;
    }
    ptr=ptr->Next;
  }
  return 0;
}
void VariableList::MakeAllReadOnly()
{
  VariableList *ptr=this;
  while(ptr!=NULL && ptr->Data.Operand!=OPER_UNKNOWN)
  {
    ptr->readOnly=1;
    ptr=ptr->Next;
  }
}
int VariableList::PartOfAnyVariable(Variable &v1,Variable &v2,int &v2Type)
{
  VariableList *ptr=this;
  while(ptr && ptr->Data.Operand!=OPER_UNKNOWN)
  {
    if (ptr->Data.SameKind(v1))
    {
      Word addr2 = ptr->Data.GetAddress();
      Word addr1 = v1.GetAddress();
      // does the given variable's address fall in the range of this variable's
      //  address range?
      addr1+=1000; addr2+=1000;		// 1000 = BP(assumed).
      if (addr1==addr2 || (addr2<=addr1 && addr2+ptr->Size>addr1))
      {
	v2=ptr->Data;
	v2Type=ptr->Type;
	return 1;
      }
    }
    ptr=ptr->Next;
  }
  return 0;
}
void VariableList::NameUnknownVariables()
{
  char prefix[3];
  VariableList *ptr=this;
  int Counters[VAR_UNKNOWN];		// VAR_UNKNOWN must be the last index
					// in the enum list, hence has the
					// size of the list.
  for(int i=0;i<VAR_UNKNOWN;i++) Counters[i]=1;
  prefix[1]='_'; prefix[2]=0;

  while(ptr && ptr->Data.Operand!=OPER_UNKNOWN)
  {
    if (strcmp(ptr->Name,"")==0)
    {
      switch(ptr->Data.Operand)
      {
	// Entry for any known function.
	case IMMEDIATE   : prefix[0]='g'; break;
	case MEMORY      : prefix[0]='g'; break;
	// for register variables.
	case REG_DIRECT  : prefix[0]='r'; break;
	case REG_INDIRECT:
	case INDEXED_BYTE:
	case INDEXED_WORD: if (ptr->Data.Data1==INDX_BP)
				prefix[0]=(ptr->Data.Data2<=0)?'l':'p';
      }
      strcpy(ptr->Name,prefix);
      strcat(ptr->Name,VariableTypes[ptr->Type]);
      strcat(ptr->Name,"_");
      itoa(Counters[ptr->Type],EndOf(ptr->Name),10);
      Counters[ptr->Type]++;
    }
    else
    {
      // check for similar existing entries like l_int_1, and increment
      //  the appropriate counter.
      for(int i=0;i<VAR_UNKNOWN;i++)
	if (ptr->Name[1]=='_' &&
	   strncmp(ptr->Name+2,VariableTypes[i],strlen(VariableTypes[i]))==0)
	   Counters[i]++;
    }
    ptr=ptr->Next;
  }
}